(ns testproj.stm)

;; STM （软件事务内存 Software Transaction Memory）基于MVCC 以事务的方式实现并发
;; dosync\ref\set\alter
;; clojure没有锁，但对不可变数据结构有间接引用
;; 三种引用:
;; (1)var
;; (2)ref 
;; (3)agent

;; 1、atoms
;; atoms是clojure的一种数据结构，它用于保持clojure的不可变数据类型
(defn example []
  (def myatom (atom 1))
  (println @myatom));; 通过@访问atom的值

(example)

;; swap更改atom值
(defn fn []
  (def myatom (atom 1))
  (println @myatom)
  
  (swap! myatom inc)
  (println @myatom))

(fn)

(def a (atom 1))

(println @a)

;; 使用reset重设atom的值
(reset! a 10000)

@a

;;可以保证原子性，无法保证有序性
(dotimes [_ 1000]
  (future (reset! a (inc @a))))

@a

(reset! a 1)

(dotimes [_ 1000]
  (future (swap! a inc)))

@a

;; 2、ref 和 agent
;; ref: 更改在线程之间进行同步
;; agent: 线程之间异步 独立变化


;; 3、dosync

;; 异常示例，在没有启动事务的情况下更改ref引用
(defn exception1 []
  (def name (ref []))
  (alter name conj "Mark"))

(exception1)

;;修改后
(defn exception2 []
  (def names (ref []))
  (dosync (alter names conj "Mark")))

(exception2)


;; dosync 事务特性示例
;; (def var1 (ref 1000 :validator #(>= % 0)))
;; (def var2 (ref 1000 :validator #(>= % 0)))
(def var1 (ref 1000))
(def var2 (ref 1000))

(defn change-value [va1 va2 vals]
  (dosync 
   (alter va1 - vals)
   (alter va2 + vals)))


(defn t1 []
  (println @var1 @var2)
  (change-value var1 var2 10)
)

(t1)



(dotimes [_ 101]
  (future (change-value var1 var2 10)))
@var1
@var2

